-- -*- coding: utf-8 -*-
-- 数据模型模块

local const = require("api.const")

local object = {}

local GameStateModel
local UnitModel
local BuildingModel

-- GameBase 基类
local GameBase = {}
GameBase.__index = GameBase

function GameBase.new(jsonObj)
    local self = setmetatable({}, GameBase)
    self._json = jsonObj or {}
    return self
end

function GameBase:__index(name)
    if name == "_json" then
        return rawget(self, "_json")
    end
    if self._json and self._json[name] then
        return self._json[name]
    end
    return GameBase[name]
end

function GameBase:toDict()
    return self._json
end

-- PlayerModel 玩家模型类
local PlayerModel = setmetatable({}, { __index = GameBase })
PlayerModel.__index = PlayerModel

function PlayerModel.new(jsonObj)
    local self = setmetatable(GameBase.new(jsonObj), PlayerModel)
    self.id = tostring(self._json.id or "")
    self.playerType = tostring(self._json.playerType or "")
    self.rmb = tonumber(self._json.rmb or 0)
    self.baseHealth = tonumber(self._json.baseHealth or 0)
    -- units
    self.units = {}
    for i, unit in ipairs(self._json.units or {}) do
        if unit.id then
            self.units[tonumber(unit.id)] = UnitModel.new(unit)
        end
    end
    -- buildings
    self.buildings = {}
    for i, building in ipairs(self._json.buildings or {}) do
        if building.id then
            local obj = BuildingModel.new(building)
            self.buildings[tonumber(building.id)] = obj
            if obj.type == const.BuildingType.base then
                self.baseBuilding = obj
            end
        end
    end
    return self
end

-- TildeModel 地块模型类
local TildeModel = setmetatable({}, { __index = GameBase })
TildeModel.__index = TildeModel

function TildeModel.new(jsonObj)
    local self = setmetatable(GameBase.new(jsonObj), TildeModel)
    self.type = self._json.type
    self.building = self._json.building or {}
    self.unit = self._json.unit or {}
    self.player = tostring(self._json.player or "")
    -- config
    self.config = const.RESOURCE_CONFIG[self.type]
    return self
end

-- GameState 游戏输入信息类
local GameState = setmetatable({}, { __index = GameBase })
GameState.__index = GameState

function GameState.new(jsonObj)
    local self = setmetatable(GameBase.new(jsonObj), GameState)
    self.battleId = tostring(self._json.battleId or "")
    self.playerId = tostring(self._json.playerId or "")
    self.round = tonumber(self._json.round or 0)
    self.timeLimit = tonumber(self._json.timeLimit or 0)
    self.battleState = GameStateModel.new(self._json.battleState or {})
    -- battleState
    self.currentRound = self.battleState.currentRound
    self.width = self.battleState.width
    self.height = self.battleState.height
    -- units
    self.units = {}
    for playerId, player in pairs(self.battleState.players) do
        for unitId, unit in pairs(player.units) do
            self.units[unitId] = unit
        end
    end
    -- buildings
    self.buildings = {}
    for playerId, player in pairs(self.battleState.players) do
        for buildingId, building in pairs(player.buildings) do
            self.buildings[buildingId] = building
        end
    end
    return self
end

function GameState:getMyPlayer()
    return self.battleState.players[self.playerId]
end

function GameState:getEnemyPlayer()
    -- 获取敌方玩家的信息
    if self.battleState.players then
        for player_id, player in pairs(self.battleState.players) do
            if player_id ~= self.playerId then
                return player
            end
        end
    end
    return nil
end

function GameState:getUnit(id)
    -- 获取指定ID的单位
    return self.units[id]
end

function GameState:getBuilding(id)
    -- 获取指定ID的建筑
    return self.buildings[id]
end

function GameState:getTilde(x, y)
    -- 获取指定位置的地块
    return self.battleState.terrain[y][x]
end

-- GameStateModel 游戏状态类
GameStateModel = setmetatable({}, { __index = GameBase })
GameStateModel.__index = GameStateModel

function GameStateModel.new(jsonObj)
    local self = setmetatable(GameBase.new(jsonObj), GameStateModel)
    self.status = tostring(self._json.status or "")
    self.initialized = self._json.initialized == true
    self.battleEnded = self._json.battleEnded == true
    self.currentRound = tonumber(self._json.currentRound or 0)
    self.width = tonumber((self._json.map or {}).width or 0)
    self.height = tonumber((self._json.map or {}).height or 0)
    -- terrain
    self.terrain = {}
    for i = 1, #self._json.map.terrain do
        local row = self._json.map.terrain[i]
        table.insert(self.terrain, {})
        for j = 1, #row do
            table.insert(self.terrain[i], TildeModel.new(row[j]))
        end
    end
    -- players
    self.players = {}
    for player_id, player_data in pairs(self._json.players) do
        self.players[player_id] = PlayerModel.new(player_data)
    end

    return self
end

-- UnitModel 单位模型类
UnitModel = setmetatable({}, { __index = GameBase })
UnitModel.__index = UnitModel

function UnitModel.new(jsonObj)
    local self = setmetatable(GameBase.new(jsonObj), UnitModel)
    self.id = tonumber(self._json.id or 0)
    self.type = tonumber(self._json.type or 0)
    self.x = tonumber(self._json.position.x or 0) + 1
    self.y = tonumber(self._json.position.y or 0) + 1
    self.hp = tonumber(self._json.hp or 0)
    self.attack = tonumber(self._json.attack or 0)
    self.speed = tonumber(self._json.speed or 0)
    -- config
    self.config = const.UNIT_CONFIG[self.type]
    return self
end

-- BuildingModel 建筑模型类
BuildingModel = setmetatable({}, { __index = GameBase })
BuildingModel.__index = BuildingModel

function BuildingModel.new(jsonObj)
    local self = setmetatable(GameBase.new(jsonObj), BuildingModel)
    self.id = tonumber(self._json.id or 0)
    self.type = tonumber(self._json.type or 0)
    self.x = tonumber(self._json.position.x or 0) + 1
    self.y = tonumber(self._json.position.y or 0) + 1
    self.hp = tonumber(self._json.hp or 0)
    self.attack = tonumber(self._json.attack or 0)
    self.player = tostring(self._json.player or "")
    -- config
    self.config = const.BUILDING_CONFIG[self.type]
    return self
end

-- CommandBase 命令基类
local CommandBase = {}
CommandBase.__index = CommandBase

function CommandBase.new(input, cmd_type, param)
    local self = setmetatable({}, CommandBase)
    self._json = {
        type = cmd_type,
        player = input.playerId,
        round = input.round,
        param = param or {}
    }
    return self
end

function CommandBase:toDict()
    return self._json
end

-- MoveCommand 移动命令类
local MoveCommand = setmetatable({}, { __index = CommandBase })
MoveCommand.__index = MoveCommand

function MoveCommand.new(input, id, x, y)
    local unit = input:getUnit(id)
    local offset_x = x - unit.x
    local offset_y = y - unit.y
    local param = {
        self = {
            type = const.EntityType.unit,
            utype = unit.type,
            id = id
        },
        offsetPosition = {
            x = offset_x,
            y = offset_y
        }
    }
    return setmetatable(CommandBase.new(input, const.CommandType.move, param), MoveCommand)
end

-- AttackCommand 攻击命令类
local AttackCommand = setmetatable({}, { __index = CommandBase })
AttackCommand.__index = AttackCommand

function AttackCommand.new(input, id, targetId, selfType, targetType)
    local attacker, target

    if selfType == const.EntityType.unit then
        attacker = input:getUnit(id)
    else
        attacker = input:getBuilding(id)
    end

    if targetType == const.EntityType.unit then
        target = input:getUnit(targetId)
    else
        target = input:getBuilding(targetId)
    end

    local offset_x = target.x - attacker.x
    local offset_y = target.y - attacker.y

    local param = {
        self = {
            type = selfType,
            utype = attacker.type,
            id = id
        },
        target = {
            id = targetId,
            type = targetType,
            utype = target.type
        },
        offsetPosition = {
            x = offset_x,
            y = offset_y
        }
    }
    return setmetatable(CommandBase.new(input, const.CommandType.attack, param), AttackCommand)
end

-- BuildCommand 建造命令类
local BuildCommand = setmetatable({}, { __index = CommandBase })
BuildCommand.__index = BuildCommand

function BuildCommand.new(input, id, utype, x, y)
    local unit = input:getUnit(id)
    local offset_x = x - unit.x
    local offset_y = y - unit.y

    local param = {
        self = {
            type = const.EntityType.unit,
            utype = unit.type,
            id = id
        },
        target = {
            type = const.EntityType.building,
            utype = utype
        },
        offsetPosition = {
            x = offset_x,
            y = offset_y
        }
    }
    return setmetatable(CommandBase.new(input, const.CommandType.build, param), BuildCommand)
end

-- SwapCommand 生产命令类
local SwapCommand = setmetatable({}, { __index = CommandBase })
SwapCommand.__index = SwapCommand

function SwapCommand.new(input, id, utype, x, y)
    local building = input:getBuilding(id)
    local offset_x = x - building.x
    local offset_y = y - building.y

    local param = {
        self = {
            type = const.EntityType.building,
            utype = building.type,
            id = id
        },
        target = {
            type = const.EntityType.unit,
            utype = utype
        },
        offsetPosition = {
            x = offset_x,
            y = offset_y
        }
    }
    return setmetatable(CommandBase.new(input, const.CommandType.swap, param), SwapCommand)
end

-- HealCommand 治疗命令类
local HealCommand = setmetatable({}, { __index = CommandBase })
HealCommand.__index = HealCommand

function HealCommand.new(input, id, targetId)
    local unit = input:getUnit(id)
    local targetUnit = input:getUnit(targetId)
    local offset_x = targetUnit.x - unit.x
    local offset_y = targetUnit.y - unit.y

    local param = {
        self = {
            type = const.EntityType.unit,
            utype = unit.type,
            id = id
        },
        target = {
            type = const.EntityType.unit,
            utype = targetUnit.type,
            id = targetId
        },
        offsetPosition = {
            x = offset_x,
            y = offset_y
        }
    }
    return setmetatable(CommandBase.new(input, const.CommandType.heal, param), HealCommand)
end

-- CollectCommand 采集命令类
local CollectCommand = setmetatable({}, { __index = CommandBase })
CollectCommand.__index = CollectCommand

function CollectCommand.new(input, id, x, y)
    local unit = input:getUnit(id)
    local tilde = input:getTilde(x, y)
    local offset_x = x - unit.x
    local offset_y = y - unit.y

    local param = {
        self = {
            type = const.EntityType.unit,
            utype = unit.type,
            id = id
        },
        target = {
            type = const.EntityType.terrain,
            utype = tilde.type
        },
        offsetPosition = {
            x = offset_x,
            y = offset_y
        }
    }
    return setmetatable(CommandBase.new(input, const.CommandType.collect, param), CollectCommand)
end

-- 导出所有类
object.GameBase = GameBase
object.PlayerModel = PlayerModel
object.TildeModel = TildeModel
object.GameState = GameState
object.GameStateModel = GameStateModel
object.UnitModel = UnitModel
object.BuildingModel = BuildingModel
object.CommandBase = CommandBase
object.MoveCommand = MoveCommand
object.AttackCommand = AttackCommand
object.BuildCommand = BuildCommand
object.SwapCommand = SwapCommand
object.HealCommand = HealCommand
object.CollectCommand = CollectCommand

return object
